<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>2D vs 3D 动画效果对比</title>
  <!-- 使用更可靠的Three.js CDN -->
  <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/build/three.min.js"></script>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    }

    body {
      background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
      color: #fff;
      min-height: 100vh;
      padding: 20px;
      overflow-x: hidden;
    }

    header {
      text-align: center;
      margin-bottom: 30px;
      padding: 20px;
    }

    h1 {
      font-size: 2.8rem;
      margin-bottom: 10px;
      background: linear-gradient(90deg, #ff8a00, #e52e71);
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
      text-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
    }

    .subtitle {
      font-size: 1.2rem;
      color: #a0a0c0;
      max-width: 800px;
      margin: 0 auto;
      line-height: 1.6;
    }

    .container {
      display: flex;
      flex-wrap: wrap;
      justify-content: center;
      gap: 30px;
      max-width: 1400px;
      margin: 0 auto;
    }

    .panel {
      flex: 1;
      min-width: 500px;
      background: rgba(30, 30, 60, 0.7);
      border-radius: 15px;
      overflow: hidden;
      box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
      backdrop-filter: blur(10px);
      border: 1px solid rgba(255, 255, 255, 0.1);
    }

    .panel-header {
      background: rgba(40, 40, 80, 0.8);
      padding: 15px 25px;
      display: flex;
      justify-content: space-between;
      align-items: center;
      border-bottom: 1px solid rgba(255, 255, 255, 0.1);
    }

    .panel-title {
      font-size: 1.5rem;
      font-weight: 600;
    }

    .tech-badge {
      background: linear-gradient(90deg, #6a11cb 0%, #2575fc 100%);
      padding: 5px 12px;
      border-radius: 20px;
      font-size: 0.8rem;
      font-weight: 600;
    }

    .canvas-container {
      position: relative;
      width: 100%;
      height: 500px;
      overflow: hidden;
      display: flex;
      align-items: center;
      justify-content: center;
    }

    canvas {
      display: block;
      width: 100%;
      height: 100%;
    }

    .controls {
      padding: 20px;
      display: flex;
      flex-wrap: wrap;
      gap: 15px;
      background: rgba(25, 25, 50, 0.6);
    }

    button {
      background: linear-gradient(90deg, #4776E6 0%, #8E54E9 100%);
      border: none;
      color: white;
      padding: 12px 25px;
      border-radius: 8px;
      cursor: pointer;
      font-weight: 600;
      transition: all 0.3s ease;
      box-shadow: 0 4px 15px rgba(142, 84, 233, 0.3);
    }

    button:hover {
      transform: translateY(-2px);
      box-shadow: 0 6px 20px rgba(142, 84, 233, 0.4);
    }

    button:disabled {
      background: #666;
      cursor: not-allowed;
      transform: none;
      box-shadow: none;
    }

    .info-section {
      max-width: 1400px;
      margin: 40px auto 0;
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 30px;
    }

    .info-card {
      background: rgba(30, 30, 60, 0.7);
      border-radius: 15px;
      padding: 25px;
      box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
      backdrop-filter: blur(10px);
      border: 1px solid rgba(255, 255, 255, 0.1);
    }

    .info-card h3 {
      font-size: 1.5rem;
      margin-bottom: 15px;
      color: #8E54E9;
    }

    .info-card ul {
      list-style-type: none;
    }

    .info-card li {
      margin-bottom: 10px;
      padding-left: 20px;
      position: relative;
    }

    .info-card li:before {
      content: "▶";
      position: absolute;
      left: 0;
      color: #4776E6;
      font-size: 0.8rem;
    }

    footer {
      text-align: center;
      margin-top: 50px;
      padding: 20px;
      color: #a0a0c0;
      font-size: 0.9rem;
    }

    .webgl-error {
      text-align: center;
      padding: 20px;
      color: #ff6b6b;
    }

    .webgl-error a {
      color: #4dabf7;
      text-decoration: none;
    }

    .webgl-error a:hover {
      text-decoration: underline;
    }

    @media (max-width: 1100px) {
      .container {
        flex-direction: column;
      }

      .panel {
        min-width: 100%;
      }

      .info-section {
        grid-template-columns: 1fr;
      }
    }
  </style>
</head>
<body>
<header>
  <h1>2D vs 3D 动画效果对比</h1>
  <p class="subtitle">探索Canvas 2D与WebGL/Three.js在动画实现上的差异与优势，了解不同技术栈的适用场景。</p>
</header>

<div class="container">
  <div class="panel">
    <div class="panel-header">
      <div class="panel-title">2D Canvas 动画</div>
      <div class="tech-badge">Canvas 2D API</div>
    </div>
    <div class="canvas-container">
      <canvas id="canvas2d"></canvas>
    </div>
    <div class="controls">
      <button id="btn2d-explosion">触发粒子爆炸</button>
      <button id="btn2d-fireworks">烟花效果</button>
      <button id="btn2d-reset">重置场景</button>
    </div>
  </div>

  <div class="panel">
    <div class="panel-header">
      <div class="panel-title">3D WebGL 动画</div>
      <div class="tech-badge">Three.js / WebGL</div>
    </div>
    <div class="canvas-container" id="canvas3d-container">
      <div id="canvas3d"></div>
      <div id="webgl-error" class="webgl-error" style="display: none;">
        <h3>WebGL 初始化失败</h3>
        <p>您的浏览器可能不支持WebGL，或者WebGL已被禁用。</p>
        <p>请尝试以下解决方案：</p>
        <ul>
          <li><a href="https://get.webgl.org/" target="_blank">检查WebGL支持</a></li>
          <li>更新您的浏览器到最新版本</li>
          <li>在浏览器设置中启用硬件加速</li>
          <li>尝试使用其他浏览器（如Chrome、Firefox）</li>
        </ul>
      </div>
    </div>
    <div class="controls">
      <button id="btn3d-explosion" disabled>触发粒子爆炸</button>
      <button id="btn3d-fireworks" disabled>烟花效果</button>
      <button id="btn3d-reset" disabled>重置场景</button>
    </div>
  </div>
</div>

<div class="info-section">
  <div class="info-card">
    <h3>2D Canvas 特点</h3>
    <ul>
      <li>基于像素的2D渲染</li>
      <li>API简单直观，学习曲线平缓</li>
      <li>性能优秀，适合大量简单动画</li>
      <li>兼容性极佳，几乎所有浏览器支持</li>
      <li>适合UI动画、图表、简单游戏</li>
      <li>内存占用相对较低</li>
      <li>不支持真正的3D和光照效果</li>
    </ul>
  </div>

  <div class="info-card">
    <h3>3D WebGL 特点</h3>
    <ul>
      <li>基于GPU的3D图形渲染</li>
      <li>真正的3D空间、光照和材质</li>
      <li>复杂的视觉效果和后期处理</li>
      <li>适合复杂3D场景、产品展示、游戏</li>
      <li>学习曲线较陡，需要3D数学知识</li>
      <li>现代浏览器支持良好</li>
      <li>性能取决于GPU和场景复杂度</li>
    </ul>
  </div>
</div>

<footer>
  <p>2D vs 3D 动画效果对比展示 | 使用 Canvas 2D 和 Three.js 实现</p>
</footer>

<script>
  // ============================
  // 2D Canvas 动画实现
  // ============================
  class ParticleSystem2D {
    constructor(canvas) {
      this.canvas = canvas;
      this.ctx = canvas.getContext('2d');
      this.particles = [];
      this.resizeCanvas();

      window.addEventListener('resize', () => this.resizeCanvas());
    }

    resizeCanvas() {
      this.canvas.width = this.canvas.offsetWidth;
      this.canvas.height = this.canvas.offsetHeight;
    }

    createExplosion(x, y, color = '#FFD700') {
      const particleCount = 150;

      for (let i = 0; i < particleCount; i++) {
        const angle = Math.random() * Math.PI * 2;
        const speed = Math.random() * 5 + 2;

        this.particles.push({
          x, y,
          vx: Math.cos(angle) * speed,
          vy: Math.sin(angle) * speed,
          life: 1.0,
          decay: Math.random() * 0.02 + 0.008,
          size: Math.random() * 6 + 2,
          color: this.randomGoldenColor(),
          type: 'explosion'
        });
      }
    }

    createFireworks(x, y) {
      const colors = ['#FF5252', '#4CAF50', '#2196F3', '#FFEB3B', '#E91E63'];
      const particleCount = 200;

      for (let i = 0; i < particleCount; i++) {
        const angle = Math.random() * Math.PI * 2;
        const speed = Math.random() * 6 + 2;
        const color = colors[Math.floor(Math.random() * colors.length)];

        this.particles.push({
          x, y,
          vx: Math.cos(angle) * speed,
          vy: Math.sin(angle) * speed,
          life: 1.0,
          decay: Math.random() * 0.015 + 0.005,
          size: Math.random() * 5 + 1,
          color: color,
          type: 'firework'
        });
      }
    }

    randomGoldenColor() {
      const colors = ['#FFD700', '#FFEC8B', '#FFFACD', '#FFA500'];
      return colors[Math.floor(Math.random() * colors.length)];
    }

    update() {
      // 清除画布，添加半透明背景实现拖尾效果
      this.ctx.fillStyle = 'rgba(26, 26, 46, 0.2)';
      this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);

      for (let i = this.particles.length - 1; i >= 0; i--) {
        const p = this.particles[i];

        // 更新粒子状态
        p.x += p.vx;
        p.y += p.vy;
        p.vy += 0.1; // 重力
        p.life -= p.decay;

        // 渲染粒子
        this.ctx.save();
        this.ctx.globalAlpha = p.life;

        // 粒子发光效果
        const gradient = this.ctx.createRadialGradient(
                p.x, p.y, 0,
                p.x, p.y, p.size * 2
        );
        gradient.addColorStop(0, p.color);
        gradient.addColorStop(1, 'rgba(255, 255, 255, 0)');

        this.ctx.fillStyle = gradient;
        this.ctx.beginPath();
        this.ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
        this.ctx.fill();

        this.ctx.restore();

        // 移除死亡粒子
        if (p.life <= 0) {
          this.particles.splice(i, 1);
        }
      }

      // 继续动画循环
      if (this.particles.length > 0) {
        requestAnimationFrame(() => this.update());
      }
    }

    reset() {
      this.particles = [];
      this.ctx.fillStyle = '#1a1a2e';
      this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
    }
  }

  // 初始化2D粒子系统
  const canvas2d = document.getElementById('canvas2d');
  const particleSystem2D = new ParticleSystem2D(canvas2d);
  particleSystem2D.reset();

  // 2D按钮事件
  document.getElementById('btn2d-explosion').addEventListener('click', () => {
    const x = canvas2d.width / 2;
    const y = canvas2d.height / 2;
    particleSystem2D.createExplosion(x, y);
    particleSystem2D.update();
  });

  document.getElementById('btn2d-fireworks').addEventListener('click', () => {
    const x = Math.random() * canvas2d.width;
    const y = Math.random() * canvas2d.height / 2;
    particleSystem2D.createFireworks(x, y);
    particleSystem2D.update();
  });

  document.getElementById('btn2d-reset').addEventListener('click', () => {
    particleSystem2D.reset();
  });

  // ============================
  // 3D Three.js 动画实现
  // ============================

  // 检查WebGL支持
  function checkWebGLSupport() {
    try {
      const canvas = document.createElement('canvas');
      return !!(window.WebGLRenderingContext &&
              (canvas.getContext('webgl') || canvas.getContext('experimental-webgl')));
    } catch(e) {
      return false;
    }
  }

  // 等待Three.js加载完成
  function waitForThreeJS(callback) {
    if (window.THREE && window.THREE.Scene) {
      callback();
    } else {
      setTimeout(() => waitForThreeJS(callback), 100);
    }
  }

  // 初始化3D场景
  function init3DScene() {
    const container = document.getElementById('canvas3d');
    const errorDiv = document.getElementById('webgl-error');

    // 检查WebGL支持
    if (!checkWebGLSupport()) {
      errorDiv.style.display = 'block';
      container.innerHTML = '<div style="color: #ff6b6b; text-align: center; padding: 20px;">您的浏览器不支持WebGL</div>';
      return null;
    }

    try {
      // 创建场景
      const scene = new THREE.Scene();
      scene.background = new THREE.Color(0x1a1a2e);

      // 创建相机
      const camera = new THREE.PerspectiveCamera(
              75,
              container.clientWidth / container.clientHeight,
              0.1,
              1000
      );
      camera.position.z = 15;

      // 创建渲染器
      const renderer = new THREE.WebGLRenderer({
        antialias: true,
        alpha: true
      });
      renderer.setSize(container.clientWidth, container.clientHeight);
      container.appendChild(renderer.domElement);

      // 添加灯光
      const ambientLight = new THREE.AmbientLight(0x404040);
      scene.add(ambientLight);

      const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
      directionalLight.position.set(5, 10, 7);
      scene.add(directionalLight);

      // 创建粒子组
      const particleGroup = new THREE.Group();
      scene.add(particleGroup);

      // 添加一个简单的参考物体
      const geometry = new THREE.BoxGeometry(2, 2, 2);
      const material = new THREE.MeshBasicMaterial({
        color: 0x00ff00,
        wireframe: true
      });
      const cube = new THREE.Mesh(geometry, material);
      cube.position.y = -5;
      scene.add(cube);

      // 粒子系统
      const particles = [];

      // 动画循环
      function animate() {
        requestAnimationFrame(animate);

        // 更新粒子
        for (let i = particles.length - 1; i >= 0; i--) {
          const particle = particles[i];
          const data = particle.userData;

          // 更新位置
          particle.position.x += data.vx;
          particle.position.y += data.vy;
          particle.position.z += data.vz;

          // 应用重力
          data.vy -= 0.01;

          // 更新生命周期
          data.life -= data.decay;
          particle.material.opacity = data.life;

          // 缩放效果
          particle.scale.setScalar(data.life);

          // 移除死亡粒子
          if (data.life <= 0) {
            particleGroup.remove(particle);
            particles.splice(i, 1);
          }
        }

        // 旋转参考立方体
        cube.rotation.x += 0.01;
        cube.rotation.y += 0.01;

        // 轻微旋转相机
        camera.position.x = Math.sin(Date.now() * 0.0005) * 15;
        camera.position.z = Math.cos(Date.now() * 0.0005) * 15;
        camera.lookAt(0, 0, 0);

        renderer.render(scene, camera);
      }

      animate();

      // 窗口大小调整处理
      window.addEventListener('resize', () => {
        camera.aspect = container.clientWidth / container.clientHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(container.clientWidth, container.clientHeight);
      });

      // 返回粒子创建函数
      return {
        createExplosion: function(color = 0xFFD700) {
          const particleCount = 200;
          const geometry = new THREE.SphereGeometry(0.2, 8, 8);

          for (let i = 0; i < particleCount; i++) {
            const material = new THREE.MeshBasicMaterial({
              color: color,
              transparent: true,
              opacity: 1
            });

            const particle = new THREE.Mesh(geometry, material);

            // 随机初始位置和速度
            const angle = Math.random() * Math.PI * 2;
            const speed = Math.random() * 0.5 + 0.2;

            particle.userData = {
              vx: Math.cos(angle) * speed,
              vy: Math.sin(angle) * speed,
              vz: (Math.random() - 0.5) * speed,
              life: 1.0,
              decay: Math.random() * 0.01 + 0.005
            };

            particles.push(particle);
            particleGroup.add(particle);
          }
        },

        createFireworks: function() {
          const colors = [0xFF5252, 0x4CAF50, 0x2196F3, 0xFFEB3B, 0xE91E63];
          const particleCount = 250;
          const geometry = new THREE.SphereGeometry(0.15, 6, 6);

          for (let i = 0; i < particleCount; i++) {
            const color = colors[Math.floor(Math.random() * colors.length)];
            const material = new THREE.MeshBasicMaterial({
              color: color,
              transparent: true,
              opacity: 1
            });

            const particle = new THREE.Mesh(geometry, material);

            // 随机初始位置和速度
            const angle = Math.random() * Math.PI * 2;
            const speed = Math.random() * 0.4 + 0.1;

            particle.userData = {
              vx: Math.cos(angle) * speed,
              vy: Math.sin(angle) * speed,
              vz: (Math.random() - 0.5) * speed,
              life: 1.0,
              decay: Math.random() * 0.008 + 0.003
            };

            particles.push(particle);
            particleGroup.add(particle);
          }
        },

        reset: function() {
          for (let i = particles.length - 1; i >= 0; i--) {
            particleGroup.remove(particles[i]);
          }
          particles.length = 0;
        }
      };

    } catch (error) {
      console.error("3D场景初始化失败:", error);
      errorDiv.style.display = 'block';
      errorDiv.innerHTML = `
                    <h3>3D场景初始化失败</h3>
                    <p>错误信息: ${error.message}</p>
                    <p>请尝试以下解决方案：</p>
                    <ul>
                        <li><a href="https://get.webgl.org/" target="_blank">检查WebGL支持</a></li>
                        <li>更新您的浏览器到最新版本</li>
                        <li>在浏览器设置中启用硬件加速</li>
                        <li>尝试使用其他浏览器（如Chrome、Firefox）</li>
                    </ul>
                `;
      return null;
    }
  }

  // 等待页面加载完成后初始化3D场景
  document.addEventListener('DOMContentLoaded', function() {
    waitForThreeJS(function() {
      const particleSystem3D = init3DScene();

      if (particleSystem3D) {
        // 启用3D按钮
        document.getElementById('btn3d-explosion').disabled = false;
        document.getElementById('btn3d-fireworks').disabled = false;
        document.getElementById('btn3d-reset').disabled = false;

        // 3D按钮事件
        document.getElementById('btn3d-explosion').addEventListener('click', () => {
          particleSystem3D.createExplosion();
        });

        document.getElementById('btn3d-fireworks').addEventListener('click', () => {
          particleSystem3D.createFireworks();
        });

        document.getElementById('btn3d-reset').addEventListener('click', () => {
          particleSystem3D.reset();
        });
      }
    });
  });
</script>
</body>
</html>